package com.lagou.factory;

import com.lagou.annotation.MyAutowire;
import com.lagou.annotation.MyComponent;
import com.lagou.annotation.MyService;
import com.lagou.annotation.MyTransactional;
import com.lagou.utils.TransactionManager;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.io.File;
import java.lang.reflect.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BeanFactory {

    //存放所以被注解修饰的类的全限定名
    List<String> classNameList = new ArrayList<>();
    //bean实例对象集合，<beanId, obj>
    Map<String, Object> beanMap = new HashMap<>();

    public BeanFactory(String basePackage) throws Exception {
        doScan(basePackage);
        createBean();
        injectionBean();
        txProxy();
    }

    private void doScan(String basePackage){
        //将 com.lagou.dao 转化成  com/lagou/dao
        String packagePath = basePackage.replace(".", "/");
        //获取包的url
        URL url = BeanFactory.class.getClassLoader().getResource(packagePath);
//        System.out.println("url = " + url);
        //当前包所在文件
        assert url != null;
        File currentFile = new File(url.getPath());
        //文件不存在返回空值
        if (!currentFile.exists()) return;
        scanPackage(basePackage, currentFile);
    }

    private void scanPackage(String path, File file){
        if(file.isFile()) {
            classNameList.add(path.replace(".class", ""));
            return;
        }
        File[] files = file.listFiles();
        assert files != null;
        for(File f : files){
            scanPackage(path + "." + f.getName(), f);
        }
    }

    private void createBean() throws Exception {
        for(String className : classNameList){
            Class<?> aClass = Class.forName(className);
            //判断当前类是否被MyComponent、MyService注解修饰
            String id = null;
            if(aClass.isAnnotationPresent(MyComponent.class)){
                MyComponent annotation = aClass.getAnnotation(MyComponent.class);
                String value = annotation.value();
                id  = value.equals("")?aClass.getName().substring(aClass.getName().lastIndexOf(".")+1):value;
            }
            if(aClass.isAnnotationPresent(MyService.class)){
                MyService annotation = aClass.getAnnotation(MyService.class);
                String value = annotation.value();
                id  = value.equals("")?aClass.getName().substring(aClass.getName().lastIndexOf(".")+1):value;
            }
            if(id!=null)
                doMapBean(id, aClass);
        }
    }

    //利用反射技术生成bean实例并封装bean到map
    private void doMapBean(String id, Class<?> aClass) throws Exception {

        //获取无参构造
        Constructor<?> constructor = aClass.getConstructor();
        //实例化对象
        Object obj = constructor.newInstance();
        //封装到map
        beanMap.put(id, obj);
    }

    //依赖注入
    private void injectionBean() throws IllegalAccessException {
        for (Map.Entry<String, Object> entry : beanMap.entrySet()){
            Object obj = entry.getValue();
            Field[] fields = obj.getClass().getDeclaredFields();
            for(Field field : fields){
                if(field.isAnnotationPresent(MyAutowire.class)){
                    field.setAccessible(true);
                    MyAutowire annotation = field.getAnnotation(MyAutowire.class);
                    field.set(obj, beanMap.get(annotation.value()));
                }
            }
        }
    }

    public Object getBean(String id){
        return beanMap.get(id);
    }

    //事务管理
    private void txProxy(){
        for (Map.Entry<String, Object> entry : beanMap.entrySet()) {
            Class<?> aClass = entry.getValue().getClass();
            //用于 标记 类/方法是否被@MyTransactional注解修饰
            boolean flag = false;
            if (aClass.isAnnotationPresent(MyTransactional.class)) {
                flag=true;
            }else {
                Method[] methods = aClass.getMethods();
                for(Method method : methods){
                    MyTransactional annotation = method.getAnnotation(MyTransactional.class);
                    if(annotation!=null){
                        flag=true;
                    }
                }
            }
            if(flag){
                doProxy(aClass);
            }
        }
    }

    private void doProxy(Class<?> aClass){
        MyService annotation = aClass.getAnnotation(MyService.class);
        Object o = beanMap.get(annotation.value());
        Object proxyObj = null;
        //判断是否实现了接口
        if(annotation.flag()){
            proxyObj = getProxyObj(o);
        }else {
            proxyObj = getCglibProxyObj(o);
        }
        beanMap.put(annotation.value(), proxyObj);
    }

    //jdbc动态代理方式获取target的代理对象
    public Object getProxyObj(Object target){
        Object o = Proxy.newProxyInstance(BeanFactory.class.getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("jdbcProxy");
                boolean annotationPresent = target.getClass().isAnnotationPresent(MyTransactional.class);
                MyTransactional annotation = method.getAnnotation(MyTransactional.class);
                Object result = null;
                String id = null;
                //判断是否开启事务
                if(annotationPresent){
                    //类被@MyTransactional注解修饰，需要开启事务
                    id = target.getClass().getAnnotation(MyTransactional.class).value();
                }else if(annotation!=null){
                    //类未被@MyTransactional注解修饰，但方法被修饰，需要开启事务
                    id = annotation.value();
                }
                if(id==null)
                    return method.invoke(target, args);
                try {
                    //开启事务
                    TransactionManager.begin();
                    //业务逻辑
                    result = method.invoke(target, args);
                    //提交事务
                    TransactionManager.commit();
                    return result;
                } catch (Exception e) {
                    e.printStackTrace();
                    //回滚事务
                    TransactionManager.rollback();
                    throw e;
                }
            }
        });
        return o;
    }

    //cglib动态代理
    public Object getCglibProxyObj(Object target){
        return Enhancer.create(target.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("cglibProxy");
                boolean annotationPresent = target.getClass().isAnnotationPresent(MyTransactional.class);
                MyTransactional annotation = method.getAnnotation(MyTransactional.class);
                Object result = null;
                String id = null;
                //判断是否开启事务
                if(annotationPresent){
                    //类被@MyTransactional注解修饰，需要开启事务
                    id = target.getClass().getAnnotation(MyTransactional.class).value();
                }else if(annotation!=null){
                    //类未被@MyTransactional注解修饰，但方法被修饰，需要开启事务
                    id = annotation.value();
                }
                if(id==null)
                    return method.invoke(target, objects);
                try {
                    //开启事务
                    TransactionManager.begin();
                    result = method.invoke(target, objects);
                    //提交事务
                    TransactionManager.commit();
                    return result;
                } catch (Exception e) {
                    e.printStackTrace();
                    //回滚事务
                    TransactionManager.rollback();
                    throw e;
                }
            }
        });
    }
}
